import java.util.*;
import java.util.regex.*;
import java.util.function.Function;

public class SensitiveDataMasker {

    // 更精确的正则表达式
    private static final String NAME_REGEX = "(?<![\\u4e00-\\u9fa5])([\u4e00-\u9fa5]{2,4})(?![\\u4e00-\\u9fa5])";
    private static final String PHONE_REGEX = "(?<!\\d)(?:(?:1[3-9]\\d{9})|(?:0\\d{2,3}-?\\d{7,8}))(?!\\d)";
    private static final String ADDRESS_REGEX = "(?<![\\u4e00-\\u9fa5])[\u4e00-\u9fa5]{2,3}[市省区县][\u4e00-\u9fa5]{2,50}[路街道号室](?![\\u4e00-\\u9fa5])";
    private static final String ID_CARD_REGEX = "(?<!\\d)\\d{17}[\\dXx](?!\\d)";
    private static final String EMAIL_REGEX = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b";

    private final MaskingConfig config;

    public SensitiveDataMasker(MaskingConfig config) {
        this.config = config;
    }

    public String mask(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }

        String masked = input;
        masked = maskPattern(masked, NAME_REGEX, this::maskChineseName);
        masked = maskPattern(masked, PHONE_REGEX, this::maskPhone);
        masked = maskPattern(masked, ADDRESS_REGEX, this::maskAddress);
        masked = maskPattern(masked, ID_CARD_REGEX, this::maskIdCard);
        masked = maskPattern(masked, EMAIL_REGEX, this::maskEmail);

        return masked;
    }

    private String maskPattern(String input, String regex, Function<String, String> maskFunction) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            String match = matcher.group();
            if (!config.getExceptions().contains(match)) {
                matcher.appendReplacement(sb, Matcher.quoteReplacement(maskFunction.apply(match)));
            }
        }
        matcher.appendTail(sb);

        return sb.toString();
    }

    private String maskChineseName(String name) {
        int length = name.length();
        if (length == 2) {
            return name.charAt(0) + "*";
        } else if (length == 3) {
            return name.charAt(0) + "*" + name.charAt(2);
        } else {
            return name.substring(0, 2) + "*".repeat(length - 2);
        }
    }

    private String maskPhone(String phone) {
        return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }

    private String maskAddress(String address) {
        int length = address.length();
        int maskLength = Math.min(length - 4, 6);
        return address.substring(0, 2) + "*".repeat(maskLength) + address.substring(length - 2);
    }

    private String maskIdCard(String idCard) {
        return idCard.substring(0, 6) + "*".repeat(8) + idCard.substring(14);
    }

    private String maskEmail(String email) {
        int atIndex = email.indexOf('@');
        String localPart = email.substring(0, atIndex);
        String domainPart = email.substring(atIndex);
        
        if (localPart.length() > 2) {
            localPart = localPart.charAt(0) + "*".repeat(localPart.length() - 2) + localPart.charAt(localPart.length() - 1);
        } else {
            localPart = "*".repeat(localPart.length());
        }
        
        return localPart + domainPart;
    }

    public static void main(String[] args) {
        MaskingConfig config = new MaskingConfig();
        config.addException("张三"); // 添加例外，不会被脱敏

        SensitiveDataMasker masker = new SensitiveDataMasker(config);

        String input = "用户张三的手机号是13812345678，住址是北京市朝阳区阳光小区1号楼3单元303室，" +
                       "身份证号440123199001011234，邮箱是zhangsan@example.com。" +
                       "订单号12345678901，清算号123456789012345678。" +
                       "另一位用户李四的信息也应该被脱敏。";

        String masked = masker.mask(input);
        System.out.println(masked);
    }
}

class MaskingConfig {
    private Set<String> exceptions;

    public MaskingConfig() {
        this.exceptions = new HashSet<>();
    }

    public void addException(String exception) {
        exceptions.add(exception);
    }

    public Set<String> getExceptions() {
        return exceptions;
    }
}